﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NHibernate;
using Peak.Core.DataInterfaces;
using Peak.Core;
using SharpArch.Data.NHibernate;
using NHibernate.LambdaExtensions;
using NHibernate.Criterion;
using System.Linq;

namespace Peak.Data
{
  public class TaskRepository : NHibernateRepositoryWithTypedId<Task, int>,ITaskRepository
  {
    public Page<Task> GetPage(TaskFilter filter)
    {
      ICriteria cFilter = Session.CreateCriteria(typeof(Task));

      cFilter.Add<Task>(f=>f.Project.Id == filter.ProjectId);

      if (filter.Assigned != null && filter.Assigned.Value)
      {        
        cFilter.Add(SqlExpression.IsNotNull<Task>(f=>f.AssignedTo));
      }
      if (filter.Assigned != null && !filter.Assigned.Value)
      {
        cFilter.Add(SqlExpression.IsNull<Task>(f => f.AssignedTo));
      }

      if (filter.AssignedTo != null)
      {
        cFilter.CreateAlias("AssignedTo", "u");
        cFilter.Add(Expression.InsensitiveLike("u.UserName", filter.AssignedTo));
      }

      if(filter.SprintId != null)
      {
        cFilter.CreateAlias("SprintStory", "s");
        cFilter.Add(Expression.Eq("s.Sprint.Id", filter.SprintId.Value));        
      }

      if (filter.QuickFilter != null && filter.QuickFilter.Trim() != string.Empty)
      {
        string[] splited = filter.QuickFilter.Split(' ');        
        
        Disjunction disjunction = Restrictions.Disjunction();
        Conjunction nameConjunction = Restrictions.Conjunction();
        foreach (string s in splited)
        {
          nameConjunction.Add(SqlExpression.InsensitiveLike<Task>(p => p.Name, s, MatchMode.Anywhere));
        }
        disjunction.Add(nameConjunction);

        Conjunction descConjunction = Restrictions.Conjunction();
        foreach (string s in splited)
        {
          descConjunction.Add(SqlExpression.InsensitiveLike<Task>(p => p.Description, s, MatchMode.Anywhere));
        }
        disjunction.Add(descConjunction);

        Conjunction categoryConjunction = Restrictions.Conjunction();
        foreach (string s in splited)
        {
          categoryConjunction.Add(SqlExpression.InsensitiveLike<Task>(p => p.Category, s, MatchMode.Anywhere));
        }
        disjunction.Add(categoryConjunction);

        Conjunction idConjunction = Restrictions.Conjunction();
        bool ok = false;
        foreach (string s in splited)
        {
          int id;
          if (int.TryParse(s, out id))
          {
            idConjunction.Add<Task>(f => f.Id == id);
            ok = true;
          }
        }
        if(ok)
        {
          disjunction.Add(idConjunction);
        }

        cFilter.Add(disjunction);
      }

      if (filter.Status != 0)
      {
        Disjunction disjunction = Restrictions.Disjunction();
        if ((filter.Status & FilterTaskStatus.ToDo) == FilterTaskStatus.ToDo)
          disjunction.Add<Task>(f => f.Status == TaskStatus.ToDo);
        if ((filter.Status & FilterTaskStatus.Done) == FilterTaskStatus.Done)
          disjunction.Add<Task>(f => f.Status == TaskStatus.Done);
        if ((filter.Status & FilterTaskStatus.InProgress) == FilterTaskStatus.InProgress)
          disjunction.Add<Task>(f => f.Status == TaskStatus.InProgress);
        if ((filter.Status & FilterTaskStatus.ReadyForTest) == FilterTaskStatus.ReadyForTest)
          disjunction.Add<Task>(f => f.Status == TaskStatus.ReadyForTest);
        cFilter.Add(disjunction);
      }

      ICriteria cCount = cFilter.Clone() as ICriteria;
      cCount.SetProjection(Projections.RowCount());

      if (filter.PageSize.HasValue)
      {
        cFilter.SetFirstResult(filter.Page * filter.PageSize.Value);
        cFilter.SetMaxResults(filter.PageSize.Value);
      }

      Func<string, Order> order = Order.Asc;
      if (filter.SortDirection == SortDirection.Desc)
        order = Order.Desc;

      switch (filter.SortOrder)
      {       
        case TaskSortOrder.Status:
          cFilter.AddOrder<Task>(f => f.Status, order).AddOrder<Task>(f => f.Name, order);
          break;
        default:
          cFilter.AddOrder<Task>(f => f.Name, order);
          break; 
      }

      IList list2 = cCount.List();

      Page<Task> page = new Page<Task>();
      page.Results = cFilter.List<Task>().ToArray();
      page.TotalCount = (int)(list2[0]);
      return page;
    }   
  }
}
